package com.kfm.web.config;

import com.kfm.web.security.JwtAuthenticationFilter;
import com.kfm.web.security.MyAccessDeniedHandler;
import com.kfm.web.security.MyAuthenticationEntryPoint;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

/**
 * spring security配置
 */
@Configuration
@EnableWebSecurity // 开启Spring Security
// 开启方法级别的权限控制, securedEnabled = true 开启 @Secured 注解过滤权限, prePostEnabled = true 开启 @PreAuthorize 注解过滤权限
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private UserDetailsService userDetailsService; // 自定义用户认证逻辑

    @Resource
    private MyAuthenticationEntryPoint myAuthenticationEntryPoint; // 未登录时的处理逻辑

    @Resource
    private JwtAuthenticationFilter jwtAuthenticationFilter; // JWT 认证过滤器

    @Resource
    private MyAccessDeniedHandler myAccessDeniedHandler; // 处理无权限访问的异常


    /**
     * 解决 无法直接注入 AuthenticationManager
     *
     * @return AuthenticationManager 对象
     * @throws Exception 异常
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 配置安全拦截机制
     *
     * @param http the {@link HttpSecurity}
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                // 用于配置直接放行的请求
                .antMatchers("/user/login").permitAll()
                .antMatchers("/user/register").permitAll()
                .antMatchers("/captcha").permitAll()
                .antMatchers("/getKey").permitAll()
                .antMatchers("/images/**").permitAll()
                .antMatchers("/products/**").permitAll()
                .antMatchers("/member/login").permitAll()
                .antMatchers("/member/register").permitAll()
                .antMatchers("/cart/**").permitAll()
                .antMatchers("/order/**").permitAll()
                .antMatchers("/alipay/**").permitAll()
                .antMatchers("/statistics/**").permitAll()
                // 其余请求都需要验证
                .anyRequest().authenticated()
                // 授权码模式需要 会弹出默认自带的登录框
                .and().httpBasic().disable()
                // 禁用跨站伪造
                .csrf().disable()
                // 禁用 session
                .sessionManagement().disable()
                // 异常处理
                .exceptionHandling()
                .accessDeniedHandler(myAccessDeniedHandler) // 处理无权限访问的异常
                .authenticationEntryPoint(myAuthenticationEntryPoint); // 未登录时的处理逻辑
        // 添加 JWT 认证过滤器
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 配置密码加密器
     * 这里我们使用官方推荐的加密方式 BCryptPasswordEncoder
     * 如果要自定义密码加密器, 实现 PasswordEncoder 接口即可
     *
     * @return BCryptPasswordEncoder
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 身份认证接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        // 用户认证service 实现了 UserDetailsService 接口
        provider.setUserDetailsService(userDetailsService);
        // 设置密码加密算法
        provider.setPasswordEncoder(passwordEncoder());
        auth.authenticationProvider(provider);
    }
}
